/**
 * ulm-request
 * @description API请求模块管理，包含loading配置、请求拦截、错误处理等功能
 * @author [学答也](https://gitee.com/liming_88)
 * @version 2.0.0
 */

import Request from 'luch-request';
import {
	baseUrl,
	apiPath
} from '@/ulm/config';
import $store from '@/ulm/store';
import {
	getToken
} from '../utils/auth';

/************************ 常量定义 ************************/

/**
 * HTTP状态码常量 
 * @constant 
 * @type {Object}
 * @property {number} SUCCESS - 业务成功状态码(1)
 * @property {number} BAD_REQUEST - 错误请求(400)
 * @property {number} UNAUTHORIZED - 未授权(401)
 * @property {number} FORBIDDEN - 禁止访问(403)
 * @property {number} NOT_FOUND - 资源未找到(404)
 * @property {number} TIMEOUT - 请求超时(408)
 * @property {number} TOO_MANY_REQUESTS - 请求过于频繁(429)
 * @property {number} SERVER_ERROR - 服务器错误(500)
 */
const HTTP_STATUS = {
	SUCCESS: 1,
	BAD_REQUEST: 400,
	UNAUTHORIZED: 401,
	FORBIDDEN: 403,
	NOT_FOUND: 404,
	TIMEOUT: 408,
	TOO_MANY_REQUESTS: 429,
	SERVER_ERROR: 500
};

/**
 * 默认错误消息
 * @constant
 * @type {Object}
 */
const DEFAULT_ERROR_MESSAGES = {
	[HTTP_STATUS.BAD_REQUEST]: '请求错误',
	[HTTP_STATUS.UNAUTHORIZED]: '请先登录',
	[HTTP_STATUS.FORBIDDEN]: '拒绝访问',
	[HTTP_STATUS.NOT_FOUND]: '请求出错',
	[HTTP_STATUS.TIMEOUT]: '请求超时',
	[HTTP_STATUS.TOO_MANY_REQUESTS]: '请求频繁, 请稍后再访问',
	[HTTP_STATUS.SERVER_ERROR]: '服务器开小差啦,请稍后再试~',
	DEFAULT: '网络请求出错'
};

/************************ 默认配置 ************************/

/**
 * 请求默认配置选项 
 * @constant 
 * @type {Object}
 * @property {boolean} [showSuccess=false] - 是否显示操作成功消息
 * @property {string} [successMsg=''] - 成功提醒消息
 * @property {boolean} [showError=true] - 是否显示失败消息
 * @property {string} [errorMsg=''] - 失败提醒消息 
 * @property {boolean} [showLoading=false] - 是否显示loading
 * @property {string} [loadingMsg='加载中'] - loading文字 
 * @property {boolean} [auth=false] - 是否需要授权
 * @property {boolean} [show401Msg=false] - 是否需要登录提示
 * @property {boolean} [catch=false] - 是否捕获完整响应 
 */
const defaultOptions = {
	showSuccess: false,
	successMsg: '',
	showError: true,
	errorMsg: '',
	showLoading: false,
	loadingMsg: '加载中',
	auth: false,
	show401Msg: true,
	catch: false
};

/************************ Loading管理 ************************/

/**
 * Loading全局实例
 * @typedef {Object} LoadingInstance
 * @property {Object|null} target - Loading实例
 * @property {number} count - Loading计数器 
 * @property {number|null} timer - 定时器ID
 */
const LoadingInstance = {
	target: null,
	count: 0,
	timer: null
};

/**
 * 显示Toast提示 
 * @function 
 * @param {Object} options - Toast配置 
 * @param {string} options.title  - 提示内容 
 * @param {string} [options.icon='none'] - 图标 
 * @param {number} [options.duration=2000] - 显示时长 
 * @param {boolean} [options.mask=false] - 是否显示透明蒙层
 */
function showToast(options) {
	if (!options.title) return;
	uni.showToast({
		icon: 'none',
		duration: 2000,
		...options
	});
}

/**
 * 显示错误Toast提示
 * @function
 * @param {string} message - 错误消息
 */
function showErrorToast(message) {
	showToast({
		title: message,
		mask: true
	});
}

/**
 * 关闭Loading效果 
 * @function 
 * @description 使用防抖机制避免频繁开关Loading 
 */
function closeLoading() {
	if (LoadingInstance.timer) clearTimeout(LoadingInstance.timer);

	LoadingInstance.timer = setTimeout(() => {
		if (LoadingInstance.count > 0) LoadingInstance.count--;
		if (LoadingInstance.count === 0 && LoadingInstance.target) {
			uni.hideLoading();
			LoadingInstance.target = null;
		}
	}, 200);
}

/************************ 请求实例 ************************/

/**
 * 创建请求实例 
 * @constant 
 * @type {Request}
 */
const http = new Request({
	baseURL: baseUrl, // 基础URL 
	timeout: 8000, // 超时时间8秒
	method: 'GET', // 默认请求方法 
	header: { // 默认请求头
		'Content-Type': 'application/json;charset=UTF-8',
	},
	// #ifdef APP-PLUS 
	sslVerify: false, // APP端关闭SSL验证
	// #endif 
	// #ifdef H5 
	withCredentials: false, // H5端不携带凭证 
	// #endif
	custom: defaultOptions, // 自定义配置
});

/************************ 请求拦截器 ************************/

/**
 * 请求拦截器
 * @description 处理认证、Loading、Token等
 */
http.interceptors.request.use(
	(config) => {
		// 检查是否需要认证且未登录
		if (config.custom.auth && !$store('user').isLogin) {
			return Promise.reject(new Error('未登录'));
		}

		// 显示Loading效果
		if (config.custom.showLoading) {
			if (LoadingInstance.count === 0) {
				LoadingInstance.target = uni.showLoading({
					title: config.custom.loadingMsg,
					mask: true,
					fail: () => {
						LoadingInstance.count = 0;
						LoadingInstance.target = null;
					},
				});
			}
			LoadingInstance.count++;
		}

		// 添加Token到请求头
		const token = getToken();
		if (token) {
			config.header['Authorization'] = token;
			config.header['token'] = token;
		}

		return config;
	},
	(error) => {
		return Promise.reject(error);
	},
);

/************************ 响应处理 ************************/

/**
 * 处理成功响应 
 * @function 
 * @param {Object} response - 响应对象 
 * @returns {Promise} 处理后的Promise 
 */
function handleResponse(response) {
	const {
		config: {
			custom
		},
		data,
		header
	} = response;

	// 更新Token 
	if (header?.authorization || header?.Authorization) {
		$store('user').setToken(header.authorization || header.Authorization);
	}

	// 关闭Loading 
	custom.showLoading && closeLoading();

	// 业务错误处理 
	if (data.code !== HTTP_STATUS.SUCCESS) {
		if (custom.showError) {
			showErrorToast(data.msg || DEFAULT_ERROR_MESSAGES.DEFAULT);
		}
		return Promise.resolve(data);
	}

	// 成功提示 
	if (data.code === HTTP_STATUS.SUCCESS && data.msg && custom.showSuccess) {
		showToast({
			title: custom.successMsg || data.msg
		});
	}

	// 根据配置返回完整响应或数据部分
	return Promise.resolve(custom?.catch ? data : data.data ?? {});
}

/************************ 错误处理 ************************/
let loginMsgInstanceCount = 0
/**
 * 处理请求错误
 * @function
 * @param {Object} error - 错误对象 
 * @returns {Promise} 拒绝的Promise
 */
function handleError(error) {
	if (!error) return Promise.reject(error);

	const {
		statusCode,
		config,
		errMsg
	} = error;
	const {
		custom = defaultOptions
	} = config || {};
	const userStore = $store('user');

	// 401未授权特殊处理
	if (statusCode === HTTP_STATUS.UNAUTHORIZED) {
		if (custom.show401Msg === false) {
			userStore.logout(true);
			custom.showLoading && closeLoading();
			return Promise.reject(error);
		}

		// 根据登录状态显示不同消息 
		const errorMessage = userStore.isLogin ?
			'您的登陆已过期' :
			'请先登录';
		if (loginMsgInstanceCount === 0) {
			userStore.logout(true);
			showErrorToast(errorMessage);
			setTimeout(() => loginMsgInstanceCount = 0, 2000)
		}
		loginMsgInstanceCount++;
		return Promise.reject(error);
	}

	// 显示错误消息
	const errorMessage = getErrorMessage(error);
	showErrorToast(error.data?.msg || errorMessage);

	// 关闭Loading
	custom.showLoading && closeLoading();
	return Promise.reject(error);
}

/**
 * 根据错误对象获取错误消息
 * @function
 * @param {Object} error - 错误对象
 * @returns {string} 错误消息
 */
function getErrorMessage(error) {
	const {
		statusCode,
		errMsg
	} = error;

	// 根据状态码返回对应消息
	if (statusCode && DEFAULT_ERROR_MESSAGES[statusCode]) {
		return DEFAULT_ERROR_MESSAGES[statusCode];
	}

	// 超时错误 
	if (errMsg.includes('timeout')) return '请求超时';

	// H5网络错误
	// #ifdef H5
	if (errMsg.includes('Network')) {
		return window.navigator.onLine ? '服务器异常' : '请检查您的网络连接';
	}
	// #endif

	// 默认错误消息 
	return DEFAULT_ERROR_MESSAGES.DEFAULT;
}

/************************ 响应拦截器 ************************/

// 添加响应拦截器
http.interceptors.response.use(handleResponse, handleError);

/************************ 请求方法 ************************/

/** 
 * 基础请求方法 
 * @function 
 * @param {Object|string} config - 请求配置或URL字符串
 * @returns {Promise} 请求Promise
 */
const request = (config) => {
	if (config.url[0] !== '/') {
		config.url = apiPath + config.url;
	}
	return http.middleware(config);
};
/**
 * 文件上传方法
 * @function
 * @param {Object} config - 上传配置
 * @returns {Promise} 上传Promise
 */
export const upload = (config) => {
	if (config.url[0] !== '/') {
		config.url = apiPath + config.url;
	}
	return http.upload(config.url, config)
}
export default request;